﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Windows.Threading;
using System.Linq;
using ImageUploaderComponent.Extensions;

namespace ImageUploaderComponent.Data
{
    public class IsolatedStorageDataService : IDataService{
        private const string StoreFileName = "DemoData.xml";
        private  readonly Random random;
        private  readonly Dictionary<DispatcherTimer, TimerStruct> currentlyRunningTimers = new Dictionary<DispatcherTimer, TimerStruct>();

        private List<Type> knownTypes = new List<Type>{typeof(List<MediaData>),
                                                 typeof(List<ImageData>),typeof(List<VideoData>),typeof(MediaData),typeof(ImageData),typeof(VideoData)};

        public IsolatedStorageDataService(){
            random = new Random(0);    
        }



        public void DeleteFile(Guid itemKey) {

        }

        public void ArchiveItem(Guid itemKey) {

        }

        public ResultStruct Rollback(Guid transactionKey, List<Guid> imagesToRollback) {
            throw new NotImplementedException();
        }




        public void Dispose() {
            //nothing for now
        }


        public ResultStruct Commit(Guid transactionKey, List<Guid> imagesToCommit){
            return new ResultStruct(UploadStatus.Success);
        }

        public List<MediaData> GetImagesForSkill(int skillId){
            using (var store = IsolatedStorageFile.GetUserStoreForSite()) {
                List<MediaData> imageList;
                using (var inputStream = store.OpenFile(StoreFileName, FileMode.OpenOrCreate)) {
                    if (inputStream.Length == 0) { return new List<MediaData>(); }
                    imageList = deserialize<MediaData>(inputStream).ToList();
                    
                }
                foreach (var imageData in imageList) {
                    using (var inputStream = store.OpenFile(imageData.FileName, FileMode.OpenOrCreate)) {
                        imageData.ImageBytes = inputStream.ToArray();
                    }
                }
                return imageList;
            }
        }

        private  void writeFileToStore(IsolatedStorageFile store, byte[] streamAsByteArray, string fileName){
            if (store.FileExists(fileName))
            {
                store.DeleteFile(fileName);
            }

            using (var outputStream = store.CreateFile(fileName))
            {
                outputStream.Write(streamAsByteArray, 0, streamAsByteArray.Length);
            }
        }

        public Guid BeginTransaction(string clientId, int userId, int skillKeyId){
            return Guid.NewGuid();
        }


        public void BeginUploadFile( MediaData imageData, Action<Guid, double> updateProgress, Action<Guid, ResultStruct> updateComplete){
            var dispatcherTimer = new DispatcherTimer();

            int perSecond = random.Next(5, 100);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, perSecond);
            dispatcherTimer.Tick += dispatcherTimer_Tick;
            lock (currentlyRunningTimers) {
                var timerStruct = new TimerStruct { AccumulatedTicks = 0, UpdateComplete = updateComplete, UpdateProgress = updateProgress, ThisImage = imageData.Key};
                currentlyRunningTimers.Add(dispatcherTimer, timerStruct);
                dispatcherTimer.Start();

            }
        }


        private void dispatcherTimer_Tick(object sender, EventArgs e) {
            var timer = (DispatcherTimer) sender;
            TimerStruct timerStruct = currentlyRunningTimers[timer];
            lock(currentlyRunningTimers){
                timerStruct.AccumulatedTicks += 1;
                currentlyRunningTimers[timer] = timerStruct;
                timerStruct.UpdateProgress.Invoke(timerStruct.ThisImage,timerStruct.AccumulatedTicks);
                if(timerStruct.AccumulatedTicks >=100){
                    timerStruct.UpdateComplete(timerStruct.ThisImage, new ResultStruct(UploadStatus.Success)); 
                    timer.Stop();
                    currentlyRunningTimers.Remove(timer);
                }
            }
        }

        private  IEnumerable<T> deserialize<T>(IsolatedStorageFileStream inputStream) {
            try{
                var serializer = new DataContractSerializer(typeof (IEnumerable<MediaData>),knownTypes);
                object deserialized = serializer.ReadObject(inputStream);
                return ((IEnumerable<T>)deserialized);
            }
// ReSharper disable EmptyGeneralCatchClause
            catch(Exception){
// ReSharper restore EmptyGeneralCatchClause
                //eat the error on purpose. to allow invalid old file formats to die gracefully
            }
            return new ObservableCollection<T>();
        }

        private  MemoryStream serializeData<T>(ObservableCollection<T> dataToSave){
            var memoryStream = new MemoryStream();
            var serializer = new DataContractSerializer(typeof(IEnumerable<T>),knownTypes);
            serializer.WriteObject(memoryStream, dataToSave);
            return memoryStream;
        }

        /// <summary>
        /// Pay no attention to that method behind the curtain. I need this as a hack to make isolated storage work.
        /// </summary>
        /// <param name="uploaderViewModel"></param>
        /// <param name="skillId"></param>
        public void CommitImageMetaData(UploaderViewModel uploaderViewModel, int skillId) {
            foreach (var imageData in uploaderViewModel.ImageDataList) {
                imageData.IsDeletable = false;
            }
            ObservableCollection<MediaData> mediaList = uploaderViewModel.ImageDataList.Select(iv => iv.DataItem).ToObservable();
            using (MemoryStream memoryStream = serializeData(mediaList)) {
                memoryStream.Seek(0, 0);
                byte[] streamAsByteArray = memoryStream.ToArray();
                memoryStream.Seek(0, 0);

                using (var store = IsolatedStorageFile.GetUserStoreForSite()) {
                    writeFileToStore(store, streamAsByteArray, StoreFileName);

                    foreach (ImageDataViewModel imageData in uploaderViewModel.ImageDataList) {
                        byte[] fileByteArray;
                        if (imageData.ImageFileInfo != null) {
                            fileByteArray = imageData.ImageFileInfo.OpenRead().ToArray();
                        }
                        else {
                            fileByteArray = imageData.ImageBytes;
                        }
                        writeFileToStore(store, fileByteArray, imageData.FileName);
                    }
                }
            }
        }
    }
    public struct TimerStruct{
        public int AccumulatedTicks { get; set; }
        public Action<Guid, double> UpdateProgress { get; set; }
        public Action<Guid, ResultStruct> UpdateComplete { get; set; }
        public Guid ThisImage { get; set; }
    }
}
